package game;

import java.util.ArrayList;
import java.util.Observable;

import datastructures.Coordinate;
import datastructures.EdgeWord;
import datastructures.Window;
import datastructures.Word;
import datastructures.World;

/**
 * @author Fredrik
 * 
 */
public class MainModel extends Observable {
	private World world;
	private Coordinate currentWindowCordinates = new Coordinate(0, 0);
	private boolean editing = false;

	/**
	 * Constructor creating a new model with a world that has the dimensions 6,6
	 */
	public MainModel() {
		this.world = new World(5, 5);
		this.currentWindowCordinates = new Coordinate(2, 2);
	}

	/**
	 * Constructor creating a model with the world w
	 * 
	 * @param w
	 */
	public MainModel(World w) {
		this.world = w;
	}

	/**
	 * Sets the world used by this model
	 * 
	 * @param world
	 */
	public void setWorld(World world) {
		this.world = world;
	}

	/**
	 * Returns the coordinates for the current window selected
	 * 
	 * @return the coordinates for the current window selected
	 */
	public Coordinate getCurrentWindowCordinates() {
		return (Coordinate) this.currentWindowCordinates.clone();
	}

	/**
	 * Sets the current selected window coordinates
	 * 
	 * @param windowCordinates
	 */
	public void setCurrentWindowCordinates(Coordinate windowCordinates) {
		this.currentWindowCordinates = windowCordinates;
	}

	/**
	 * Add a word to the window that is selected
	 * 
	 * @param w
	 * @return true if the add was successful
	 */
	public boolean addWordToWindow(Word w) {
		if (getCurrentWindow().addWord(w)) {
			changed();
			return true;
		}
		return false;

	}

	/**
	 * Guess at every word in the current selected window
	 * 
	 * @param guess
	 * @return true if the guess was successful
	 */
	public boolean guessAtWord(String guess) {
		return getCurrentWindow().guessAtWord(guess);
	}

	/**
	 * Call changed when anything has changed in the model
	 */
	public void changed() {
		setChanged();
		notifyObservers();
	}

	/**
	 * returns a list of words connected to the selected window
	 * 
	 * @return a list of words connected to the selected window
	 */
	public ArrayList<Word> getWordsForCurrentWindow() {
		return getCurrentWindow().getWords();
	}

	/**
	 * Adds a edge word to the currently selected window
	 * 
	 * @param edgeWord
	 * @return true if the word was successfully added
	 */
	public boolean addEdgeWord(EdgeWord edgeWord) {

		Coordinate otherC = new Coordinate(this.currentWindowCordinates.getX(),
				this.currentWindowCordinates.getY());
		if (edgeWord.getEdge() == EdgeWord.WEST_EDGE) {
			if (this.currentWindowCordinates.getX() == 0) {
				return false;
			}
			otherC.setX(this.currentWindowCordinates.getX() - 1);

		} else if (edgeWord.getEdge() == EdgeWord.NORTH_EDGE) {
			if (this.currentWindowCordinates.getY() == 0) {
				return false;
			}
			otherC.setY(this.currentWindowCordinates.getY() - 1);

		} else if (edgeWord.getEdge() == EdgeWord.EAST_EDGE) {
			if (this.currentWindowCordinates.getX() == this.world.getWidth() - 1) {
				return false;
			}
			otherC.setX(this.currentWindowCordinates.getX() + 1);

		} else if (edgeWord.getEdge() == EdgeWord.SOUTH_EDGE) {
			if (this.currentWindowCordinates.getY() == this.world.getHeight() - 1) {
				return false;
			}
			otherC.setY(this.currentWindowCordinates.getY() + 1);

		}

		if (getCurrentWindow().wordExistInWindow(edgeWord)
				&& getWindowByCoordinate(otherC).wordExistInWindow(edgeWord)) {
			getCurrentWindow().addWord(edgeWord);
			getWindowByCoordinate(otherC).addWord(edgeWord.getTwinWord());
			return true;
		}

		return false;
	}

	private Window getCurrentWindow() {
		return this.world.getWindow(this.currentWindowCordinates);
	}

	private Window getWindowByCoordinate(Coordinate c) {
		return this.world.getWindow(c);
	}

	/**
	 * Returns the width of the world
	 * 
	 * @return the width of the world
	 */
	public int getWidth() {
		return this.world.getWidth();
	}

	/**
	 * Returns the height of the world
	 * 
	 * @return the height of the world
	 */
	public int getHeight() {
		return this.world.getHeight();
	}

	/**
	 * Returns true if the model is set to editing mode
	 * 
	 * @return true if the model is set to editing mode
	 */
	public boolean getEditingMode() {
		return this.editing;
	}

	/**
	 * Set to true if the model should work in editing mode
	 * 
	 * @param editing
	 */
	public void setEditingMode(boolean editing) {
		this.editing = editing;
	}

	/**
	 * Returns the world used by this model
	 * @return the world used by this model
	 */
	 public World getWorld() {
		 return this.world;
	 }

	/**
	 * Deletes a edge word w from the currently selected window and the twin
	 * word from the connecting window
	 * 
	 * @param w
	 * @return true if the word was successfully deleted
	 */
	public boolean deleteWord(EdgeWord w) {
		Coordinate c = getCurrentWindowCordinates();
		switch (w.getEdge()) {
		case EdgeWord.EAST_EDGE:
			c.setX(c.getX() + 1);
			break;
		case EdgeWord.WEST_EDGE:
			c.setX(c.getX() - 1);
			break;
		case EdgeWord.NORTH_EDGE:
			c.setY(c.getY() - 1);
			break;
		case EdgeWord.SOUTH_EDGE:
			c.setY(c.getY() + 1);
			break;
		}
		if (this.world.getWindow(c).deleteWord(w)) {
			boolean b = getCurrentWindow().deleteWord(w);
			return b;
		}
		return false;

	}

	/**
	 * Delete a word from the currently selected window
	 * 
	 * @param w
	 * @return true if the word was successfully deleted
	 */
	public boolean deleteWord(Word w) {
		if (w instanceof EdgeWord) {
			return deleteWord((EdgeWord) w);
		}
		return getCurrentWindow().deleteWord(w);
	}

	/**
	 * Check if the window is unlocked.
	 * 
	 * @param c
	 * @return true if a word is visible in the window at coordinate c
	 */
	public boolean isWindowUnlocked(Coordinate c) {
		return this.world.getWindow(c).isVisible();
	}

}
